---
title: 测试你的提供者程序
---

import { AutoSnippet, When } from "@site/src/components/CodeSnippet";
import unitTest from "!!raw-loader!/docs/essentials/testing/unit_test.dart";
import widgetTest from "!!raw-loader!/docs/essentials/testing/widget_test.dart";
import fullWidgetTest from "!!raw-loader!/docs/essentials/testing/full_widget_test.dart";
import testerContainer from "!!raw-loader!/docs/essentials/testing/tester_container.dart";
import providerToMock from "/docs/essentials/testing/provider_to_mock";
import mockProvider from "!!raw-loader!/docs/essentials/testing/mock_provider.dart";
import autoDisposeListen from "!!raw-loader!/docs/essentials/testing/auto_dispose_listen.dart";
import listenProvider from "!!raw-loader!/docs/essentials/testing/listen_provider.dart";
import awaitFuture from "!!raw-loader!/docs/essentials/testing/await_future.dart";
import notifierMock from "/docs/essentials/testing/notifier_mock";

<!---
A core part of the Riverpod API is the ability to test your providers in isolation.
-->
Riverpod API 的核心部分是能够单独测试提供者程序。

<!---
For a proper test suite, there are a few challenges to overcome:
-->
对于一个合适的测试套件，有几个挑战需要克服：

<!---
- Tests should not share state. This means that new tests should
  not be affected by the previous tests.
- Tests should give us the ability to mock certain functionalities
  to achieve the desired state.
- The test environment should be as close as possible to the real
  environment.
-->
- 测试不应共享状态。这意味着新测试不应受到先前测试的影响。
- 测试应该使我们能够模拟某些功能以达到所需的状态。
- 测试环境应尽可能接近真实环境。

<!---
Fortunately, Riverpod makes it easy to achieve all of these goals.
-->
幸运的是，Riverpod 可以轻松实现所有这些目标。

<!---
## Setting up a test
-->
## 设置测试

<!---
When defining a test with Riverpod, there are two main scenarios:
-->
使用 Riverpod 定义测试时，主要有两种情况：

<!---
- Unit tests, usually with no Flutter dependency.
  This can be useful for testing the behavior of a provider in isolation.
- Widget tests, usually with a Flutter dependency.
  This can be useful for testing the behavior of a widget that uses a provider.
-->
- 单元测试，通常没有 Flutter 依赖。
  这对于单独测试提供者程序的行为非常有用。
- Widget 测试，通常带有 Flutter 依赖项。
  这对于测试使用提供者程序的小部件的行为非常有用。

<!---
### Unit tests
-->
### 单元测试

<!---
Unit tests are defined using the `test` function from [package:test](https://pub.dev/packages/test).
-->
单元测试是使用 [package:test](https://pub.dev/packages/test) 中的 `test` 函数定义的。

<!---
The main difference with any other test is that we will want to create
a `ProviderContainer` object. This object will enable our test to interact
with providers.
-->
与任何其他测试的主要区别在于，我们想要创建一个 `ProviderContainer` 对象。
此对象将使我们的测试能够与提供者程序进行交互。

<!---
Then, we can define a `test` using this utility:
-->
然后，我们可以使用此实用程序定义一个 `test`：

<AutoSnippet raw={unitTest} />

<!---
Now that we have a ProviderContainer, we can use it to read providers using:
-->
现在我们有了 ProviderContainer，我们可以使用它来读取提供者程序，使用：

<!---
- `container.read`, to read the current value of a provider.
- `container.listen`, to listen to a provider and be notified of changes.
-->
- `container.read`，以读取提供者程序的当前值。
- `container.listen`，以监听提供者程序并接收更改的通知。

:::caution
<!---
Be careful when using `container.read` when providers are automatically disposed.  
If your provider is not listened to, chances are that its state will get destroyed
in the middle of your test.
-->
在自动处置提供者程序时使用 `container.read` 时要小心。  
如果您的提供者程序没有被监听，其状态可能会在测试过程中被破坏。

<!---
In that case, consider using `container.listen`.  
Its return value enables reading the current value of provider anyway,
but will also ensure that the provider is not disposed in the middle of your test:
-->
在这种情况下，请考虑使用 `container.listen`。  
无论如何，它的返回值都能读取提供者程序的当前值，
同时还能确保提供者程序不会在测试过程中被弃置：

<AutoSnippet raw={autoDisposeListen} />
:::

<!---
### Widget tests
-->
### 小部件测试

<!---
Widget tests are defined using the `testWidgets` function from [package:flutter_test](https://pub.dev/packages/flutter_test).
-->
小部件测试是使用 [package:flutter_test](https://pub.dev/packages/flutter_test) 中的 `testWidgets` 函数定义的。

<!---
In this case, the main difference with usual Widget tests is that we must add
a `ProviderScope` widget at the root of `tester.pumpWidget`:
-->
在这种情况下，与通常的 Widget 测试的主要区别在于，
我们必须添加一个 `ProviderScope` 在 `tester.pumpWidget` 的根组件上：

<AutoSnippet raw={widgetTest} />

<!---
This is similar to what we do when we enable Riverpod in our Flutter app.
-->
这类似于我们在 Flutter 应用程序中启用 Riverpod 时所做的。

<!---
Then, we can use `tester` to interact with our widget.
Alternatively if you want to interact with providers, you can obtain
a `ProviderContainer`.
One can be obtained using `ProviderScope.containerOf(buildContext)`.  
By using `tester`, we can therefore write the following:
-->
然后，我们可以用 `tester` 来与我们的小部件进行交互。
或者，如果要与提供者程序交互，可以获取 `ProviderContainer`。
也可以使用 `ProviderScope.containerOf(buildContext)` 获得一个。  
因此，通过使用 `tester` ，我们可以编写以下内容：

<AutoSnippet raw={testerContainer} />

<!---
We can then use it to read providers. Here's a full example:
-->
然后，我们可以使用它来读取提供者程序。下面是一个完整的示例：

<AutoSnippet raw={fullWidgetTest} />

<!---
## Mocking providers
-->
## 模拟提供者程序

<!---
So far, we've seen how to set up a test and basic interactions with providers.
However, in some cases, we may want to mock a provider.
-->
到目前为止，我们已经了解了如何设置测试以及与提供者程序的基本交互。
但是，在某些情况下，我们可能想要模拟一个提供者程序。

<!---
The cool part: All providers can be mocked by default, without any additional setup.  
This is possible by specifying the `overrides` parameter on either
`ProviderScope` or `ProviderContainer`.
-->
很酷的部分：默认情况下可以模拟所有提供者程序，无需任何额外设置。  
这可以通过在 `ProviderScope` 或 `ProviderContainer` 上指定 `overrides` 参数来实现。

<!---
Consider the following provider:
-->
请考虑以下提供者程序：

<AutoSnippet {...providerToMock} />

<!---
We can mock it using:
-->
我们可以模拟它通过以下方式：

<AutoSnippet raw={mockProvider} />

<!---
## Spying on changes in a provider
-->
## 监视提供者程序中的更改

<!---
Since we obtained a `ProviderContainer` in our tests, it is possible to
use it to "listen" to a provider:
-->
由于我们在测试中获得了一个 `ProviderContainer`，因此可以使用它来“监听”提供者程序：

<AutoSnippet raw={listenProvider} />

<!---
You can then combine this with packages such as [mockito](https://pub.dev/packages/mockito)
or [mocktail](https://pub.dev/packages/mocktail) to use their `verify` API.  
Or more simply, you can add all changes in a list and assert on it.
-->
然后，您可以将其与 [mockito](https://pub.dev/packages/mockito)
或 [mocktail](https://pub.dev/packages/mocktail) 等包结合使用，以使用它们的 `verify` API。  
或者更简单地说，您可以在列表中添加所有更改并对其进行断言。

<!---
## Awaiting asynchronous providers
-->
## 等待异步提供者程序

<!---
In Riverpod, it is very common for providers to return a Future/Stream.  
In that case, chances are that our tests need to await for that asynchronous operation
to be completed.
-->
在 Riverpod 中，提供者程序返回 Future/Stream 是很常见的。  
在这种情况下，我们的测试可能需要等待异步操作完成。

<!---
One way to do so is to read the `.future` of a provider:
-->
一种方法是读取提供者程序的 `.future`： 

<AutoSnippet raw={awaitFuture} />

<!---
## Mocking Notifiers
-->
## 模拟通知者程序

<!---
It is generally discouraged to mock Notifiers.  
Instead, you should likely introduce a level of abstraction in the logic of your
Notifier, such that you can mock that abstraction.
For instance, rather than mocking a Notifier, you could mock a "repository"
that the Notifier uses to fetch data from.
-->
通常不鼓励嘲笑通知者程序。  
相反，您可能应该在通告程序的逻辑中引入一个抽象级别，以便您可以模拟该抽象。
例如，与其模拟通告程序，不如模拟通告程序用来从中获取数据的“存储库”。

<!---
If you insist on mocking a Notifier, there is a special consideration
to create such a mock: Your mock must subclass the original Notifier
base class: You cannot "implement" Notifier, as this would break the interface.
-->
如果您坚持要模拟通告程序，那么创建这样的通告程序需要特别注意：
您的模拟必须对原始通告程序基类进行子类化：
您不能“实现”通告程序，因为这会破坏接口。

<!---
As such, when mocking a Notifier, instead of writing the following mockito code:
-->
因此，在模拟通告程序时，不要编写以下模拟代码：

```dart
class MyNotifierMock with Mock implements MyNotifier {}
```

<!---
You should instead write:
-->
你应该改写：

<AutoSnippet {...notifierMock} />

<When codegen={true}>

<!---
For this to work, your mock will have to be placed in the same file as the
Notifier you are mocking. Otherwise you would not have access to the `_$MyNotifier` class.
-->
为此，您的模拟必须与您模拟的通知者程序放在同一个文件中。
否则，您将无法访问该 `_$MyNotifier` 类。

</When>
